#pragma once

#include <AVUIAnimatable.h>
#include <AVUIMILUtilities.h>
#include <AVUIMatrix3D.h>
#include <AVUIRect3D.h>
#include <AVUIGeneralTransform.h>

namespace AVUI {

// REVIEW !! THIS SHOULD BE BASED ON FREEZABLE, REWRITE ONCE COMPLETE!
class Transform;

/// <summary>Provides 3-D to 2-D transformation support.</summary>
class GeneralTransform3DTo2D : public Animatable
{
public:
    DECLARE_ELEMENT(GeneralTransform3DTo2D, Animatable);

#pragma region Public Methods

    /// <summary>Transforms the specified 3-D point and returns the result.</summary>
    /// <returns>The result of transforming <paramref name="point" />.</returns>
    /// <param name="point">The 3-D point to transform.</param>
    /// <exception cref="T:System.InvalidOperationException">The transform did not succeed.</exception>
    Point Transform(const Point3D& point)
    {
        Point point2;
        if (!TryTransform(point, &point2))
        {
            LibraryThrowException(object_allocate<InvalidOperationException>("General transform failed."));
        }
        return point2;
    }

    /// <summary>Transforms the specified 3-D bounding box and returns an axis-aligned bounding box that contains all of the points in the original 3-D bounding box.</summary>
    /// <returns>An axis-aligned bounding box that contains all of the points in the specified 3-D bounding box.</returns>
    /// <param name="rect3D">The 3-D bounding box to transform.</param>
    Rect TransformBounds(const Rect3D& rect3D)
    {
        if (m_pTransformBetween2D != NULL)
        {
            return m_pTransformBetween2D->TransformBounds(MILUtilities::ProjectBounds(m_projectionTransform, rect3D));
        }
        return Rect::get_Empty();
    }

    /// <summary>Transforms the specified 3-D point and returns the result.</summary>
    /// <returns>true if <paramref name="inPoint" /> was transformed; otherwise, false.</returns>
    /// <param name="inPoint">The 3-D point to transform.</param>
    /// <param name="result">The result of transforming <paramref name="inPoint" />.</param>
    bool TryTransform(const Point3D& inPoint, Point* pResult)
    {
        bool flag = false;
        *pResult = Point();
        Point3D pointd = m_projectionTransform.Transform(inPoint);
        if (m_pTransformBetween2D != NULL)
        {
            *pResult = m_pTransformBetween2D->Transform(Point(pointd.get_X(), pointd.get_Y()));
            flag = true;
        }
        return flag;
    }
#pragma endregion Public Methods

protected:

#pragma region Protected Methods

    virtual TRefCountedPtr<Freezable> CreateInstanceCore() AVUI_OVERRIDE { return object_allocate<GeneralTransform3DTo2D>().get_Ptr(); }

#pragma endregion Protected Methods

public:

#pragma region Internal Methods

    GeneralTransform3DTo2D(const Matrix3D& projectionTransform, GeneralTransform* pTransformBetween2D) : m_projectionTransform(projectionTransform), m_pTransformBetween2D(pTransformBetween2D) { }
    GeneralTransform3DTo2D() { }

#pragma endregion Internal Methods

private:

#pragma region Private Fields

    Matrix3D m_projectionTransform;
    TRefCountedPtr<GeneralTransform> m_pTransformBetween2D; 

#pragma endregion Private Fields

};


}; // namespace AVUI
